# Python 趣味编程：第 2 章　
#    例題 2.3  球和墙壁的建模
# (3) 多个球
# --------------------------
# 程序名: 02-ball-5.py

from tkinter import *
from dataclasses import dataclass
import time

# 将参数集中在一处
DURATION = 0.001    # sleep 时间 = 绘制的间隔

@dataclass
class Ball:
    id: int
    x: int
    y: int
    d: int
    vx: int
    c: str

@dataclass
class Border:
    left: int
    right: int
    top: int
    bottom: int

# 将球绘制到初始位置，返回生成的“字典”
# 直径 d 若省略则为 3，颜色 c 若省略则为 "black"
def make_ball(x, y, d=3, vx=2, c="black"):
    id = canvas.create_rectangle(x, y, x + d, y + d,
                                 fill=c, outline=c)
    return Ball(id, x, y, d, vx, c)

# 从程序主体中抽出球的移动
def move_ball( ball ):
    ball.x = ball.x + ball.vx

# 定义绘制墙壁的函数
def make_walls(ox, oy, width, height):
    canvas.create_rectangle(ox, oy, ox + width, oy + height)

# 一个重画球的函数，封装了 coords
def redraw_ball(ball):
    d = ball.d
    canvas.coords(ball.id, ball.x, ball.y,
                  ball.x + d, ball.y + d)

tk = Tk()
canvas = Canvas(tk, width=800, height=600, bd=0)
canvas.pack()
tk.update()

# 给出墙壁的坐标(left, right, top, bottom)
border = Border(100, 700, 100, 500)

# 初始化处理
make_walls(
    border.left,
    border.top,
    border.right - border.left,
    border.bottom - border.top
    )

# 准备一个有多个球的“列表”
balls = [
    make_ball(100, 150, 20, 2, "darkblue"),
    make_ball(200, 250, 25, -4, "orange"),
    make_ball(300, 350, 10, -2, "green"),
    make_ball(400, 450, 5, 4, "darkgreen")
    ]

while True:
    for ball in balls:   # 对所有的球，循环处理
        move_ball( ball )   # 首先，使球移动
        # 如果球碰到墙壁
        if (ball.x + ball.vx < border.left \
            or ball.x + ball.d  >= border.right):
            ball.vx = - ball.vx    # 反转球的移动方向
        redraw_ball( ball )  # 绘制球的移动
    tk.update()              # 绘制被反映到画面上
    time.sleep(DURATION)     # 一直 sleep，直到下一次绘制
